<script setup lang="ts">
/**
 * 一个通用的表单页面,包含表格,新增和编辑,只适合做没有和其他表关联的表单
 */
import {FormInstance} from "element-plus";
import {openConfirm} from "@/components/MyComponent/XMessageBox";
import {cloneDeep, initPageQuery} from "@/utils/common-fn";
import {CommonTable} from "@/components/MyComponent/CommonTable/tableInterface";
import {DisplayTableType, OpenFormType} from "@/utils/enum-type";
import {useGetOptions} from "@/utils/use/use-get-options";
import {useSysStore} from "@/store/modules/sys";

defineSlots<{
  operation(props: {
    row: Record<string, any>
  }): any
}>()
// table和编辑的时候有id  add的时候没有id
const props = defineProps<{
  options: CommonTable,
}>()
const {api, attrs, title, perms, form, queryData} = props.options
const sysStore = useSysStore()
const {isPhone} = storeToRefs(sysStore)

/**
 * 处理默认值
 */
// 默认attrs
const computedAttrs = computed(() => {
      let index = 0;
      return attrs.map((attr) => {
        let newAttr = {...attr, index: -1}
        const {formType, columnWidth, sort, query, notDisplayOn, placeholder, code, image = {}} = attr
        newAttr.formType = formType ?? 'input'
        newAttr.columnWidth = columnWidth ?? 100
        if (['select', 'radio', 'custom-select'].includes(newAttr.formType)) {
          newAttr.columnWidth = 80
        }
        newAttr.sort = sort ?? true
        newAttr.query = query ?? true
        newAttr.query = newAttr.formType === 'image' ? false : newAttr.query
        newAttr.placeholder = placeholder ?? ''
        if (!Array.isArray(notDisplayOn)) {
          newAttr.notDisplayOn = []
        }
        newAttr.image = {
          ...{
            size: 3,
            path: '',
            borderRadius: '0.75em'
          },
          ...image
        }
        // 这个index是为了保证拿到的selectOptions能正确的得到index
        if (code) {
          newAttr.index = index++
        }
        return newAttr
      })
    }
)
// 默认form
const computedForm = computed(() => {
  const {labelWidth, dialogWidth} = form
  form.labelWidth = labelWidth ?? (isPhone.value ? 90 : 100)
  form.dialogWidth = isPhone.value ? '100%' : (dialogWidth ?? 600)
  return form;
})
/**
 * 处理options的问题
 */
const codes = computedAttrs.value.filter(item => item.code && !item.notDisplayOn!.includes('list'))
    .map(item => item.code) as string[]
let selectOptions = useGetOptions(...codes)


type Obj = Record<string, any>
/**
 * 表单公共字段
 */
const tableData: Ref<Obj[]> = ref([])
const total = ref(0)
// queryData数据
const computedQueryData = ref(initPageQuery(cloneDeep(queryData)))

// 打开表单类型
let openFormType: Ref<OpenFormType> = ref(OpenFormType.Add)

// 添加还是修改的文字
const addOrEditWord = openFormType.value === OpenFormType.Add ? '添加' : '修改'

// 编辑表单时规则验证
const ruleFormRef = ref<FormInstance>()
// 添加/编辑按钮的loading
const loading = ref(false)
// 页面加载中
const pageLoading = ref(true)

// 弹出框:编辑|新增
const visible = ref(false)

// 弹出框浏览
const viewVisible = ref(false)

// 编辑的表单数据
const formData = ref(cloneDeep(form.initialForm))

// 搜索ref
const searchRef = ref<FormInstance>()

// 切换显示
const displayTableType: Ref<DisplayTableType> = ref(DisplayTableType.Table)

/**
 * 方法
 */
// 提交新增
async function submit(formEl: FormInstance | undefined) {
  if (!formEl) {
    return;
  }
  await formEl.validate((valid, fields) => {
    if (valid) {
      let promise: Promise<any>
      if (openFormType.value === OpenFormType.Add) {
        promise = api.add(formData.value)
      } else {
        promise = api.edit(formData.value.id, formData.value)
      }
      promise.then(() => {
        loading.value = true
        ElMessage.success(`${addOrEditWord}成功`)
        getListPlus()
        setVisible(false)
      }).finally(() => {
        loading.value = false
      })
    } else {
      console.log('错误的字段', fields)
    }
  })
}


// 设置对话框可见
function setVisible(bolValue: boolean) {
  visible.value = bolValue
}


// 获取列表
async function getList() {
  [tableData.value, total.value] = await api.getList(computedQueryData.value);
}

// 在getList的基础上初始化options数据
function getListPlus() {
  selectOptions = useGetOptions(...codes)
  getList()
}

// 打开表单
function open(): void // 打开新增
async function open(id: number): Promise<void>  // 打开编辑
async function open(id: number, isView?: boolean): Promise<void>  // 打开浏览
async function open(id?: number, isView?: boolean) {
  if (id) {
    // 编辑
    formData.value = await api.get(id)
    openFormType.value = OpenFormType.Edit
    if (isView) {
      viewVisible.value = true
      return;
    }
  } else {
    // 新增
    formData.value = cloneDeep(form.initialForm)
    openFormType.value = OpenFormType.Add
  }
  setVisible(true)
}


// 删除按钮
async function openDelete(id: number) {
  if (await openConfirm('确定删除' + title)) {
    await api.remove(id)
    ElMessage.success('删除成功')
    getListPlus()
  }
}

// 排序
async function sortTable({prop, order}: {
  column: any,
  prop: string,
  order: 'descending' | 'ascending' | null
}) {
  if (order === 'descending' || !order) {
    computedQueryData.value.order = {[prop]: 'desc'}
  } else {
    computedQueryData.value.order = {[prop]: 'asc'}
  }
  getListPlus()
}


// 重置搜索
async function resetSearch(formEl: FormInstance | undefined) {
  if (!formEl) {
    return;
  }
  formEl.resetFields()
  computedQueryData.value = initPageQuery(cloneDeep(queryData))
  await nextTick()
  getListPlus()
}

function switchDisplay() {
  if (displayTableType.value === DisplayTableType.Table) {
    displayTableType.value = DisplayTableType.Card
  } else {
    displayTableType.value = DisplayTableType.Table

  }
}

// 分页变化,查询
watch([() => computedQueryData.value.pageSize, () => computedQueryData.value.currentPage], () => {
  getListPlus()
})
// 载入查询
onMounted(async () => {
  pageLoading.value = true;
  await getList()
  pageLoading.value = false;
  // 处理options问题
})
</script>
<template>
  <div>
    <!--  查询 -->
    <x-card>
      <el-form :inline="true" :model="computedQueryData.query" ref="searchRef" label-suffix=":">
        <template v-for="item in computedAttrs">
          <el-form-item
              :style="{width:(isPhone?'45%':''),marginRight:(isPhone?'1em':'')}"
              v-if="item.query"
              :label="item.label"
              :key="item.prop"
              :prop="item.prop"
          >
            <el-input v-if="['input'].includes(item.formType!)" :placeholder="item.placeholder"
                      v-model="computedQueryData.query[item.prop]" clearable @keyup.enter="getListPlus()"/>
            <el-input v-if="['textarea'].includes(item.formType!)" :placeholder="item.placeholder"
                      v-model="computedQueryData.query[item.prop]"
                      type="textarea" autosize clearable/>
            <el-input-number :placeholder="item.placeholder" v-if="item.formType === 'number'"
                             v-model="computedQueryData.query[item.prop]"/>
            <x-dict-select v-if="['select','radio'].includes(item.formType!)"
                           v-model="computedQueryData.query[item.prop]"
                           :type-code="item.code!" type="select"/>
          </el-form-item>
        </template>
        <el-form-item>
          <el-button type="primary" @click="getListPlus()">查询</el-button>
          <el-button @click="resetSearch(searchRef)" circle>
            <i-ep-refresh-left/>
          </el-button>
        </el-form-item>
      </el-form>
    </x-card>
    <!-- 表格 -->
    <x-card v-perm="perms?.add">
      <!--    新增按钮-->
      <el-button v-perm="perms?.add" type="success" @click="open()">
        <i-ep-plus/>
        <span>新增</span>
      </el-button>
      <el-button link type="primary" @click="switchDisplay()">
        <i-ep-refresh/>
        <span>切换显示</span>
      </el-button>
    </x-card>
    <!-- table表格   -->
    <x-card v-if="displayTableType === DisplayTableType.Table">
      <el-skeleton v-if="pageLoading" :rows="15" animated/>
      <el-table v-else :data="tableData" style="width: 100%" @sort-change="sortTable">
        <template v-for="item in computedAttrs">
          <!--   当为list的时候,列表不显示;  当为list-phone的时候 ,表示为phone的时候不显示      -->
          <template
              v-if="!item.notDisplayOn!.includes('list') && (!isPhone || !item.notDisplayOn!.includes('list-phone'))">
            <el-table-column
                v-if="['input'].includes(item.formType!)"
                :prop="item.prop" :label="item.label" :min-width="item.columnWidth!"
                :sortable="item.sort? 'custom': false" align="center">
              <template #default="{row}" v-if="item.format">
                {{ item.format(row) }}
              </template>
            </el-table-column>
            <el-table-column
                v-if="['image'].includes(item.formType!)"
                :label="item.label" :min-width="60">
              <template #default="{row}">
                <x-image :url="row[item.prop]" :size="item.image?.size" :border-radius="item.image?.borderRadius"/>
              </template>
            </el-table-column>
            <el-table-column
                v-if="['number'].includes(item.formType!)"
                :prop="item.prop" :label="item.label" :min-width="item.columnWidth!"
                :sortable="item.sort? 'custom': false" align="center">
              <template #default="{row}">
                <el-statistic :value="row[item.prop]" value-style="color:#606266;font-size:16px"/>
              </template>
            </el-table-column>
            <el-table-column
                v-if="['percent'].includes(item.formType!)"
                :prop="item.prop" :label="item.label" :min-width="item.columnWidth!"
                :sortable="item.sort? 'custom': false" align="center">
              <template #default="{row}">
                <el-statistic :value="row[item.prop]/100" value-style="color:#606266;font-size:16px">
                  <template #suffix>
                    <span style="color:#606266;font-size:16px">%</span>
                  </template>
                </el-statistic>
              </template>
            </el-table-column>
            <el-table-column
                v-if="['textarea'].includes(item.formType!) && !isPhone"
                :prop="item.prop" :label="item.label" :min-width="item.columnWidth!"
                :sortable="item.sort? 'custom': false">
              <template #default="{row}">
                <el-input type="textarea" readonly autosize :model-value="row[item.prop]"/>
              </template>
            </el-table-column>
            <el-table-column
                v-if="['select','radio','custom-select'].includes(item.formType!)"
                :prop="item.prop"
                :key="item.prop"
                :label="item.label"
                :min-width="item.columnWidth!"
                :sortable="item.sort!"
                align="center"
            >
              <template #default="{row}">
                <x-tag :value="row[item.prop]" :options="selectOptions[item.index]"/>
              </template>
            </el-table-column>
          </template>
        </template>
        <el-table-column :fixed="isPhone ? false : 'right'" label="操作" :min-width="200">
          <template #default="{row,columns,$index}">
            <slot name="operation" :row="row"></slot>
            <el-button link plain size="small" @click="open(row.id , true)">
              <i-ep-view/>
              <span>查看</span>
            </el-button>
            <el-button v-perm="perms?.edit" link type="primary" size="small" @click="open(row.id)">
              <i-ep-edit/>
              <span>编辑</span>
            </el-button>
            <el-button v-perm="perms?.remove" link type="danger" size="small" @click="openDelete(row.id)">
              <i-ep-delete/>
              <span>删除</span>
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </x-card>
    <!-- 卡片显示的简单内容   -->
    <x-card v-if="displayTableType === DisplayTableType.Card">
      <!--      todo:简单卡片内容-->
      <h3>todo:卡片内容</h3>
    </x-card>
    <!--  分页-->
    <x-card>
      <el-skeleton v-if="pageLoading" :rows="1" animated/>
      <XPagination
          v-else
          v-model:page-size="computedQueryData.pageSize"
          v-model:current-page="computedQueryData.currentPage"
          v-model:total="total"
      />
    </x-card>
    <!-- 编辑或添加对话框 -->
    <el-dialog draggable v-model="visible" :title="`${addOrEditWord}${title}`" :width="computedForm.dialogWidth!"
               destroy-on-close>
      <el-form v-if="visible" :model="formData" label-suffix=":" :label-width="computedForm.labelWidth!"
               ref="ruleFormRef"
               :rules="computedForm.rules">
        <template v-for="item in computedAttrs">
          <el-form-item
              v-if="!item.notDisplayOn!.includes('add')"
              :key="item.prop"
              :label="item.label"
              :prop="item.prop"
          >
            <el-input v-if="item.formType === 'input'" v-model="formData[item.prop]"
                      :placeholder="item.placeholder"/>
            <el-input-number v-if="['number','percent'].includes(item.formType!)" v-model="formData[item.prop]"
                             :placeholder="item.placeholder"/>
            <el-input type="textarea" autosize v-if="item.formType === 'textarea'" v-model="formData[item.prop]"
                      :placeholder="item.placeholder"/>
            <x-dict-select v-if="item.formType === 'radio'" :type-code="item.code!" v-model="formData[item.prop]"/>
            <x-dict-select v-if="item.formType === 'select'" :type-code="item.code!" v-model="formData[item.prop]"
                           type="select"/>
            <x-upload v-if="item.formType === 'image'" v-model="formData[item.prop]" :path="item.image?.path"/>
            <x-select v-if="item.formType === 'custom-select'" v-model="formData[item.prop]"
                      :list-api="item.customSelect!.listApi"
                      :label="item.customSelect?.label"
                      :value="item.customSelect?.value"
                      :slot-values="item.customSelect?.slotValues"
            />
          </el-form-item>
        </template>
        <slot name="formItem" :row="formData"></slot>
      </el-form>
      <template #footer>
      <span class="dialog-footer">
        <el-button @click="setVisible(false)">取消</el-button>
        <el-button type="primary" :loading="loading" @click="submit(ruleFormRef)">
          确定
        </el-button>
      </span>
      </template>
    </el-dialog>
    <!-- 查看   -->
    <el-dialog draggable v-model="viewVisible" :title="title" :width="computedForm.dialogWidth!"
               destroy-on-close>
      <el-descriptions border :column="2">
        <el-descriptions-item
            v-for="item in computedAttrs"
            :label="item.label"
            :span="['textarea'].includes(item.formType!) ? 2 : 1"
            :key="item.prop">
          <el-text v-if="['input'].includes(item.formType!)">
            <template v-if="item.format">
              {{ item.format(formData[item.prop]) }}
            </template>
            <template v-else>
              {{ formData[item.prop] }}
            </template>
          </el-text>
          <el-statistic v-if="['percent'].includes(item.formType!)" :value="formData[item.prop]/100"
                        value-style="color:#606266;font-size:16px">
            <template #suffix>
              <span style="color:#606266;font-size:16px">%</span>
            </template>
          </el-statistic>
          <el-input v-if="['textarea'].includes(item.formType!)" type="textarea" readonly autosize
                    :model-value="formData[item.prop]"/>
          <el-statistic v-if="['number'].includes(item.formType!)" :value="formData[item.prop]"
                        value-style="color:#606266;font-size:16px"/>
          <x-image-view v-if="item.formType === 'image'" :url="formData[item.prop]" width="2em"/>
          <x-tag v-if="['radio','select'].includes(item.formType!)" :value="formData[item.prop]"
                 :options="selectOptions[item.index]"/>
        </el-descriptions-item>
      </el-descriptions>
    </el-dialog>
  </div>
</template>
<style scoped>
</style>